/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#ifndef CORE_REGIONS_INTERNALENDPOINTSPARSER_H_
#define CORE_REGIONS_INTERNALENDPOINTSPARSER_H_

#include <string>
#include <fstream>

#include "AcsError.h"
#include "ProductDomain.h"
#include "IEndpointsProvider.h"
#include "reader/XmlReader.h"

namespace aliyun {

namespace regions {

class InternalEndpointsParser : public IEndpointsProvider {
 public:
  typedef std::string string;

  static const char INTERNAL_ENDPOINTS_FILENAME[];

  static const char CURRENT_HEADER_FILE_PATH[];

  virtual std::vector<Endpoint> getEndpoints() throw (ClientException) {
    try {
      // read file content
      string content = readFile(getIternalEnpointFilePath());
      return parseEndpoints(content);
    } catch (reader::XmlException& e) {
      throw ClientException("SDK.EndpointsFileMalformed",
                            "bad format happened in internal endpoint file");
    } catch (Exception& e) {
      throw ClientException("SDK.MissingEndpointFile",
                            "internal endpoint file is missing.");
    } catch (...) {
      throw;  // unknow exception rethrow
    }
  }

  static string readFile(string filename) {
    std::ifstream file(filename.c_str());
    if (!file) {
      throw Exception("file not found");
    }

    // get file length
    file.seekg(0, std::ios_base::end);
    long length = file.tellg();
    file.seekg(0, std::ios_base::beg);

    // read file content
    string content(length, '\0');
    file.read(&content[0], length);
    return content;
  }

  // singleton usage
  static InternalEndpointsParser* getInstance() {
    if (sInstance_ == NULL) {
      static InternalEndpointsParser stub;
      sInstance_ = &stub;
    }
    return sInstance_;
  }

 private:
  static InternalEndpointsParser* sInstance_;

  static std::vector<Endpoint> parseEndpoints(const string& xml) {
    using aliyun::reader::XmlReader;
    XmlReader reader;
    XmlReader::document* doc = reader.getDocument(xml);

    std::vector<XmlReader::node*> endpointNodes =
        XmlReader::getElementsByTagName(doc, "Endpoint");

    std::vector<Endpoint> endpoints;
    for (int i = 0; i < endpointNodes.size(); i++) {
      XmlReader::node* endpoint = endpointNodes[i];
      std::vector<XmlReader::node*> regionNodes =
          XmlReader::getElementsByTagName(endpoint, "RegionId");
      std::vector<XmlReader::node*> productNodes =
          XmlReader::getElementsByTagName(endpoint, "Product");

      std::set<string> regionIds;
      for (int j = 0; j < regionNodes.size(); j++) {
        regionIds.insert(XmlReader::getContent(regionNodes[j]));
      }

      std::vector<ProductDomain> products;
      for (int j = 0; j < productNodes.size(); j++) {
        XmlReader::node* elem = productNodes[j];
        std::vector<XmlReader::node*> productNodes =
            XmlReader::getElementsByTagName(elem, "ProductName");
        std::vector<XmlReader::node*> domainNodes =
            XmlReader::getElementsByTagName(elem, "DomainName");
        for (int k = 0; k < productNodes.size(); k++) {
          string pn = XmlReader::getContent(productNodes[k]);
          string rn = XmlReader::getContent(domainNodes[k]);
          products.push_back(ProductDomain(pn, rn));
        }
      }

      string name = XmlReader::getAttribute(endpoint, "name");
      endpoints.push_back(Endpoint(name, regionIds, products));
    }
    return endpoints;
  }

  /**
   * get full path of internal endpoints file
   *
   * make sure "endpoint.xml" in the same  with "InternalEndpointsParser.h"
   * */
  string getIternalEnpointFilePath() {
    string cur(CURRENT_HEADER_FILE_PATH);
    int idx = cur.find_last_of('/');
    if (idx == string::npos) {
      idx = cur.find_last_of('\\');  // for windows
    }

    if (idx == string::npos) {
      return INTERNAL_ENDPOINTS_FILENAME;
    }
    return cur.substr(0, idx + 1) + INTERNAL_ENDPOINTS_FILENAME;
  }
};

InternalEndpointsParser* InternalEndpointsParser::sInstance_ = NULL;

const char InternalEndpointsParser::INTERNAL_ENDPOINTS_FILENAME[] =
    "endpoints.xml";

// DON'T move this constant to .cc file
const char InternalEndpointsParser::CURRENT_HEADER_FILE_PATH[] = __FILE__;

}  // namespace regions

}  // namespace aliyun

#endif /* CORE_REGIONS_INTERNALENDPOINTSPARSER_H_ */
